package com.jsgt.greekfire.common.http

import android.util.Log
import com.google.gson.Gson
import com.jsgt.greekfire.App
import com.jsgt.greekfire.BuildConfig
import com.jsgt.greekfire.LOGIN_STATUS
import com.jsgt.greekfire.event.LoginEvent
import okhttp3.*
import okio.Buffer
import org.greenrobot.eventbus.EventBus
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import java.util.logging.Logger
import kotlin.text.Charsets.UTF_8

/**
 * author fhx
 * create time 2020/4/25 13:35
 **/
class HttpLoggingInterceptor(tag: String?) : Interceptor {

    @Volatile
    private var printLevel: Level? = Level.NONE
    private var colorLevel: java.util.logging.Level? = null
    private val logger: Logger = Logger.getLogger(tag)

    enum class Level {
        NONE,  //不打印log
        BASIC,  //只打印 请求首行 和 响应首行
        HEADERS,  //打印请求和响应的所有 Header
        BODY //所有数据全部打印
    }

    fun setPrintLevel(level: Level) {
        if (printLevel == null) throw NullPointerException("printLevel == null. Use Level.NONE instead.")
        printLevel = level
    }

    fun setColorLevel(level: java.util.logging.Level?) {
        colorLevel = level
    }

    private fun log(message: String) { //logger.log(colorLevel, message);
        if (BuildConfig.DEBUG) {
            Log.e("请求数据:", message)
        }
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        if (printLevel == Level.NONE) {
            return chain.proceed(request)
        }
        //请求日志拦截
        logForRequest(request, chain.connection())
        //执行请求，计算请求时间
        val startNs = System.nanoTime()
        val response: Response
        response = try {
            chain.proceed(request)
        } catch (e: Exception) {
            log("<-- HTTP FAILED: $e")
            //failed to connect to apiv3.utios.me 像这种异常就不会运行到这里，连接失败了应该不会跑这里
            RetrofitManager.processNetException(e,request.url().toString())
            throw e
        }
        val tookMs =
            TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
        val code = response.code()
        if (code == 502) {
            RetrofitManager.switchBaseUrl(response.request().url().toString())
        }
        //响应日志拦截
        return logForResponse(response, tookMs)
    }

    @Throws(IOException::class)
    private fun logForRequest(
        request: Request,
        connection: Connection?
    ) {
        val logBody = printLevel == Level.BODY
        val logHeaders =
            printLevel == Level.BODY || printLevel == Level.HEADERS
        val requestBody = request.body()
        val hasRequestBody = requestBody != null
        val protocol =
            connection?.protocol() ?: Protocol.HTTP_1_1
        try {
            val requestStartMessage =
                "--> " + request.method() + ' ' + request.url() + ' ' + protocol + " {" + params(
                    requestBody
                ) + "}"
            log(requestStartMessage)
            if (logHeaders) {
                if (hasRequestBody) { // Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
                    if (requestBody!!.contentType() != null) {
                        // log("\tContent-Type: " + requestBody.contentType())
                    }
                    if (requestBody.contentLength() != -1L) {
                        //  log("\tContent-Length: " + requestBody.contentLength())
                    }
                }
                val headers = request.headers()
                var i = 0
                val count = headers.size()
                while (i < count) {
                    val name = headers.name(i)
                    // Skip headers from the request body as they are explicitly logged above.
                    if (!"Content-Type".equals(name, ignoreCase = true) && !"Content-Length".equals(
                            name,
                            ignoreCase = true
                        )
                    ) {
                        log("\t" + name + ": " + headers.value(i))
                    }
                    i++
                }
                log(" ")
                if (logBody && hasRequestBody) {
                    if (isPlaintext(requestBody!!.contentType())) {
                        bodyToString(request)
                    } else {
                        log("\tbody: maybe [binary body], omitted!")
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            log("--> END " + request.method())
        }
    }

    private fun logForResponse(response: Response, tookMs: Long): Response {
        //val builder = response.newBuilder()
        var responseBody = response.body()
        val logBody = printLevel == Level.BODY
        val logHeaders =
            printLevel == Level.BODY || printLevel == Level.HEADERS
        try {
            log("<-- " + response.code() + ' ' + response.message() + ' ' + response.request().url() + " (" + tookMs + "ms）")
            if (logHeaders) {
                val headers = response.headers()
                var i = 0
                val count = headers.size()
                while (i < count) {
                    log("\t" + headers.name(i) + ": " + headers.value(i))
                    i++
                }
                log(" ")
                if (responseBody == null) return response
                try {
                    if (logBody) {
                        if (isPlaintext(responseBody.contentType())) {
                            val source = responseBody.source()
                            source.request(Long.MAX_VALUE) // Buffer the entire body.
                            val buffer = source.buffer
                            var charset: Charset = UTF8
                            val body = buffer.clone().readString(charset)
                            val responseData =
                                gson.fromJson(body, ResponseData::class.java)
                            if (responseData?.code == 401 && LOGIN_STATUS) {
                                App.context.logout()
                                EventBus.getDefault().post(LoginEvent(responseData.msg!!))
                            }
                            log("\tbody:$body")
                            //responseBody = bytes.toResponseBody(responseBody.contentType())
                            //return response.newBuilder().body(responseBody).build()
                        } else {
                            val source = responseBody.source()
                            source.request(Long.MAX_VALUE) // Buffer the entire body.
                            val buffer = source.buffer
                            var charset: Charset = UTF8
                            val body = buffer.clone().readString(charset)
                            log("\tbody:$body")
                           // log("\tbody: maybe [binary body], omitted!")
                        }
                    }
                }catch (e:Exception){

                }

            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            log("<-- END HTTP")
        }
        return response
    }
    val gson = Gson()
    private fun bodyToString(request: Request) {
        try {
            val copy = request.newBuilder().build()
            val body = copy.body() ?: return
            val buffer = Buffer()
            body.writeTo(buffer)
            val charset =
                getCharset(body.contentType())
            log("\tbody:" + buffer.readString(charset!!))
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun params(body: RequestBody?): String {
        val buffer = Buffer()
        return try {
            body?.writeTo(buffer)
            var charset = Charset.forName("UTF-8")
            val contentType = body?.contentType()
            if (contentType != null) {
                charset = contentType.charset(UTF_8)
            }
            buffer.readString(charset).replace("&", ",")
        } catch (e: IOException) {
            ""
        }
    }

    @Throws(IOException::class)
    fun toByteArray(input: InputStream?): ByteArray? {
        val output = ByteArrayOutputStream()
        write(input!!, output)
        output.close()
        return output.toByteArray()
    }

    @Throws(IOException::class)
    fun write(
        inputStream: InputStream,
        outputStream: OutputStream
    ) {
        var len: Int
        val buffer = ByteArray(4096)
        while (inputStream.read(buffer).also { len = it } != -1) outputStream.write(
            buffer,
            0,
            len
        )
    }

    companion object {
        private val UTF8 = Charset.forName("UTF-8")
        private fun getCharset(contentType: MediaType?): Charset? {
            var charset =
                if (contentType != null) contentType.charset(UTF8) else UTF8
            if (charset == null) charset = UTF8
            return charset
        }

        /**
         * Returns true if the body in question probably contains human readable text. Uses a small sample
         * of code points to detect unicode control characters commonly used in binary file signatures.
         */
        private fun isPlaintext(mediaType: MediaType?): Boolean {
            if (mediaType == null) return false
            if (mediaType.type() != null && mediaType.type() == "text") {
                return true
            }
            var subtype = mediaType.subtype()
            if (subtype != null) {
                subtype = subtype.toLowerCase()
                if (subtype.contains("x-www-form-urlencoded") || subtype.contains("json") || subtype.contains(
                        "xml"
                    ) || subtype.contains("html")
                ) //
                    return true
            }
            return false
        }
    }

}